using System;
using System.Collections.Generic;
using System.IO;
using GarageGames.Torque.Core;
using GarageGames.Torque.MathUtil;
using GarageGames.Torque.SceneGraph;
using GarageGames.Torque.Sim;
using GarageGames.Torque.T3D.RigidCollision;
using GarageGames.Torque.TS;
using GarageGames.Torque.Util;
using GarageGames.Torque.XNA;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;



namespace GarageGames.Torque.T3D
{
    [TorqueXmlSchemaType]
    public class T3DAvatarRenderComponent : T3DRenderComponent, IAnimatedObject
    {

        #region Public properties, operators, constants, and enums


        #endregion


        #region Public methods

        public override void Render(SceneRenderState sceneRenderState)
        {
#if DEBUG
            Profiler.Instance.StartBlock("T3DTSRenderComponent.Render");
#endif
            base.Render(sceneRenderState);

            if (_shapeInstance != null)
            {
                Vector3 scale = Vector3.One;

                if (_transform != null)
                {
                    sceneRenderState.World.Push();
                    sceneRenderState.World.MultiplyMatrixLocal(_transform.ObjectMatrix);
                    scale = _transform.Scale;
                }

                _shapeInstance.Animate();

                _shapeInstance.Render(_shapeInstance.SelectCurrentDetail(sceneRenderState), 0, scale, sceneRenderState, _hiddenMeshes);

                if (_transform != null)
                    sceneRenderState.World.Pop();
            }

#if DEBUG
            Profiler.Instance.EndBlock("T3DTSRenderComponent.Render");
#endif
        }



        public virtual void UpdateAnimation(float dt)
        {
            if (_threadAutoAdvance == 0)
                return;

            for (int i = 0; i < _threadInterfaces.Count; i++)
            {
                if ((_threadAutoAdvance & (1 << i)) != 0)
                    _threadInterfaces[i].Value.AdvanceTime(dt);
            }
        }



        /// <summary>
        /// Create a new animation thread for the object.
        /// </summary>
        /// <param name="threadName">The name of the thread to create.</param>
        /// <param name="sequenceIndex">The index of the sequence on the shape.</param>
        /// <param name="autoAdvance">Whether or not to play the thread automatically.</param>
        /// <returns>The created thread.</returns>
        public virtual Thread AddThread(string threadName, int sequenceIndex, bool autoAdvance)
        {
            Thread thread = AddThread(threadName, null, autoAdvance);
            if (thread == null || sequenceIndex < 0 || sequenceIndex >= _shape.Sequences.Length)
                return thread;

            _shapeInstance.SetSequence(thread, sequenceIndex, 0.0f);
            return thread;
        }



        /// <summary>
        /// Creates a new animation thread for the object.
        /// </summary>
        /// <param name="threadName">The name of the thread to create.</param>
        /// <param name="startSequence">The sequence to set on the thread.</param>
        /// <param name="autoAdvance">Whether or not to play the thread automatically.</param>
        /// <returns>The created thread.</returns>
        public virtual Thread AddThread(String threadName, String startSequence, bool autoAdvance)
        {
            if (_shape == null || _shapeInstance == null || threadName == null)
                // just ignore it
                return null;

            // make sure we allocate the thread arrays
            if (_threadNames == null)
                _threadNames = new List<string>();

            if (_threadInterfaces == null)
                _threadInterfaces = new List<ValueInPlaceInterface<Thread>>();

            _threadNames.Add(threadName);
            Thread tsThread = _shapeInstance.AddThread();
            _threadInterfaces.Add(new ValueInPlaceInterface<Torque.TS.Thread>(tsThread));
            if (startSequence != null)
            {
                int sequenceIndex = _shape.FindSequence(startSequence);
                if (sequenceIndex >= 0)
                    _shapeInstance.SetSequence(tsThread, sequenceIndex, 0.0f);
            }

            if (autoAdvance)
                _threadAutoAdvance |= 1 << (_threadNames.Count - 1);

            return tsThread;
        }



        public override void CopyTo(TorqueComponent obj)
        {
            base.CopyTo(obj);

            T3DTSRenderComponent obj2 = (T3DTSRenderComponent)obj;
            //            obj2._shapeName = ShapeName;
            //            obj2.Shape = Shape;
        }

        #endregion


        #region Private, protected, internal methods

        protected override bool _OnRegister(TorqueObject owner)
        {
            if (!base._OnRegister(owner))
                return false;

            // cafTODO: we need to track whether we're added yet and
            // add ourself if someone adds an auto advance thread later
            if (_threadAutoAdvance != 0)
                ProcessList.Instance.AddAnimationCallback(owner, this);

            return true;
        }



        protected override void _RegisterInterfaces(TorqueObject owner)
        {
            base._RegisterInterfaces(owner);

            Owner.RegisterCachedInterface("transform3D", null, this, null);
            Owner.RegisterCachedInterface("tsanimation", null, this, null);
            Owner.RegisterCachedInterface("render", null, this, null);

            // cafTODO: or should we allow renaming?
            if (SceneClientName != null && SceneClientName != String.Empty)
                Owner.RegisterCachedInterface("bounds", SceneClientName, this, _objectBox);
        }



        protected override void _GetInterfaces(PatternMatch typeMatch, PatternMatch nameMatch, List<TorqueInterface> list)
        {
            if (typeMatch.TestMatch("transform3D"))
            {
                if (nameMatch.HasWildcard)
                {
                    for (int i = 0; i < _shape.Nodes.Length; i++)
                    {
                        String nodeName = _shape.Names[_shape.Nodes[i].NameIndex];

                        if (nameMatch.TestMatch(nodeName))
                        {
                            ValueInPlaceInterface<Transform3D> iface = new ValueInPlaceInterface<Transform3D>();
                            iface._value = new TSTransform3D(_shapeInstance, i);

                            T3DSceneComponent sceneComp = SceneGroup;
                            if (sceneComp != null)
                                iface._value.ParentTransform = sceneComp.Transform3D;

                            list.Add(Owner.RegisterInterface(this, iface));
                        }
                    }
                }
                else
                {
                    int nodeIndex = _shape.FindNode(nameMatch.Pattern);
                    if (nodeIndex >= 0)
                    {
                        ValueInPlaceInterface<Transform3D> iface = new ValueInPlaceInterface<Transform3D>();
                        iface._value = new TSTransform3D(_shapeInstance, nodeIndex);

                        T3DSceneComponent sceneComp = SceneGroup;
                        if (sceneComp != null)
                            iface._value.ParentTransform = sceneComp.Transform3D;

                        list.Add(Owner.RegisterInterface(this, iface));
                    }
                }
            }

            if (typeMatch.TestMatch("tsanimation") && _threadNames != null && _threadInterfaces != null)
            {
                // find a thread by name
                for (int i = 0; i < _threadNames.Count; i++)
                {
                    if (nameMatch.TestMatch(_threadNames[i]) && _threadInterfaces[i] != null && _threadInterfaces[i]._value != null)
                    {
                        if (_threadInterfaces[i].Owner == null)
                            // need to do this in case thread added after component initialized/added
                            Owner.RegisterInterface(this, _threadInterfaces[i]);
                        list.Add(_threadInterfaces[i]);
                    }
                }
            }

            if (typeMatch.TestMatch("render") && nameMatch.TestMatch(SceneGroupName))
            {
                TorqueInterfaceWrap<IRenderable3D> render = new TorqueInterfaceWrap<IRenderable3D>();
                Owner.RegisterInterface(this, render);
                list.Add(render);
            }

            base._GetInterfaces(typeMatch, nameMatch, list);
        }

        #endregion


        #region Private, protected, internal fields

        protected String _shapeName;
        protected Shape _shape;
        protected ShapeInstance _shapeInstance;
        protected List<ValueInPlaceInterface<Thread>> _threadInterfaces;
        protected List<String> _threadNames;
        protected int _threadAutoAdvance;
        protected int _hiddenMeshes;

        #endregion
    }

}
